/**
 * 聚合图层类
 * 专注于点要素聚合功能，封装聚合策略、样式配置、动态更新等逻辑
 */

import { Map as OlMap } from 'ol';
import { Vector as VectorLayer } from 'ol/layer';
import { Vector as VectorSource, Cluster } from 'ol/source';
import { Feature } from 'ol';
import { Point } from 'ol/geom';
import { BaseLayer } from './BaseLayer';
import { ClusterLayerOptions, FeatureData, Coordinate } from '../types';
import { StyleUtil } from '../utils/style';
import { DataUtil } from '../utils/data';
import { Style, Circle, Fill, Stroke, Text } from 'ol/style';

/**
 * 聚合图层类
 */
export class ClusterLayer extends BaseLayer {
  /** 图层配置 */
  protected options: ClusterLayerOptions;
  /** 矢量数据源 */
  private source: VectorSource<Feature<Point>> | null = null;
  /** 聚合数据源 */
  private clusterSource: Cluster | null = null;

  /**
   * 构造函数
   * @param options 图层配置
   */
  constructor(options: ClusterLayerOptions = {}) {
    super(options);
    this.options = {
      distance: 40,
      showCount: true,
      ...options
    };
  }

  /**
   * 创建图层
   */
  protected createLayer(): void {
    // 创建矢量数据源
    this.source = new VectorSource<Feature<Point>>();

    // 创建聚合数据源
    this.clusterSource = new Cluster({
      source: this.source,
      distance: this.options.distance || 40
    });

    // 创建矢量图层
    this.layer = new VectorLayer({
      source: this.clusterSource,
      style: (feature) => this.getClusterStyle(feature as Feature<Point>)
    });

    // 监听点击事件
    if (this.map) {
      this.map.on('click', (event) => {
        this.handleClick(event);
      });
    }
  }

  /**
   * 获取聚合样式
   * @param feature 要素
   * @returns 样式
   */
  private getClusterStyle(feature: Feature<Point>): Style {
    const features = feature.get('features') as Feature<Point>[];
    const count = features ? features.length : 1;

    // 如果是聚合点
    if (count > 1) {
      // 使用自定义样式函数或默认样式
      if (this.options.clusterStyle) {
        return this.options.clusterStyle(feature, count);
      }
      return StyleUtil.createClusterStyle(count, {
        radius: 10,
        fillColor: '#3399CC',
        strokeColor: '#fff',
        textColor: '#fff',
        fontSize: '12px'
      });
    } else {
      // 单个点
      if (this.options.pointStyle) {
        return this.options.pointStyle;
      }
      return StyleUtil.createCircleStyle({
        radius: 5,
        fillColor: '#3399CC',
        strokeColor: '#fff',
        strokeWidth: 2
      });
    }
  }

  /**
   * 处理点击事件
   * @param event 点击事件
   */
  private handleClick(event: any): void {
    if (!this.map || !this.layer) return;

    const feature = this.map.forEachFeatureAtPixel(
      event.pixel,
      (feature) => feature,
      {
        layerFilter: (layer) => layer === this.layer
      }
    );

    if (feature) {
      const features = feature.get('features') as Feature<Point>[];
      if (features && features.length > 1) {
        // 聚合点被点击，触发事件
        const coordinates = (feature.getGeometry() as Point).getCoordinates();
        this.emit('clusterclick', {
          feature,
          features,
          count: features.length,
          coordinate: DataUtil.fromOlCoordinate(coordinates as [number, number])
        });
      } else {
        // 单个点被点击
        this.emit('pointclick', {
          feature: features ? features[0] : feature,
          coordinate: DataUtil.fromOlCoordinate(
            ((features ? features[0] : feature).getGeometry() as Point).getCoordinates() as [number, number]
          )
        });
      }
    }
  }

  /**
   * 添加要素
   * @param data 要素数据
   */
  addFeature(data: FeatureData): void {
    if (!this.source) {
      throw new Error('Layer not initialized');
    }

    const feature = DataUtil.createPointFeature(data.geometry as Coordinate, data.properties);
    if (data.id !== undefined) {
      feature.setId(data.id);
    }
    this.source.addFeature(feature);
    this.emit('featureadd', feature);
  }

  /**
   * 批量添加要素
   * @param dataList 要素数据数组
   */
  addFeatures(dataList: FeatureData[]): void {
    if (!this.source) {
      throw new Error('Layer not initialized');
    }

    const features = dataList.map(data => {
      const feature = DataUtil.createPointFeature(data.geometry as Coordinate, data.properties);
      if (data.id !== undefined) {
        feature.setId(data.id);
      }
      return feature;
    });

    this.source.addFeatures(features);
    this.emit('featuresadd', features);
  }

  /**
   * 移除要素
   * @param featureId 要素ID
   */
  removeFeature(featureId: string | number): void {
    if (!this.source) return;

    const feature = this.source.getFeatureById(featureId);
    if (feature && feature instanceof Feature) {
      this.source.removeFeature(feature);
      this.emit('featureremove', feature);
    }
  }

  /**
   * 清空所有要素
   */
  clearFeatures(): void {
    if (this.source) {
      this.source.clear();
      this.emit('featuresclear');
    }
  }

  /**
   * 更新数据
   * @param dataList 要素数据数组
   */
  updateData(dataList: FeatureData[]): void {
    this.clearFeatures();
    this.addFeatures(dataList);
  }

  /**
   * 设置聚合距离
   * @param distance 聚合距离（像素）
   */
  setDistance(distance: number): void {
    this.options.distance = distance;
    if (this.clusterSource) {
      this.clusterSource.setDistance(distance);
    }
  }

  /**
   * 获取聚合距离
   * @returns 聚合距离
   */
  getDistance(): number {
    return this.options.distance || 40;
  }

  /**
   * 设置聚合点样式
   * @param styleFunction 样式函数
   */
  setClusterStyle(styleFunction: (feature: Feature, count: number) => any): void {
    this.options.clusterStyle = styleFunction;
    if (this.layer) {
      (this.layer as any).setStyle((feature: any) => this.getClusterStyle(feature as Feature<Point>));
    }
  }

  /**
   * 设置点样式
   * @param style 样式
   */
  setPointStyle(style: any): void {
    this.options.pointStyle = style;
    if (this.layer) {
      (this.layer as any).setStyle((feature: any) => this.getClusterStyle(feature as Feature<Point>));
    }
  }

  /**
   * 获取所有要素
   * @returns 要素数组
   */
  getFeatures(): Feature<Point>[] {
    return this.source?.getFeatures() || [];
  }

  /**
   * 根据ID获取要素
   * @param featureId 要素ID
   * @returns 要素
   */
  getFeatureById(featureId: string | number): Feature<Point> | null {
    const feature = this.source?.getFeatureById(featureId);
    return feature instanceof Feature ? feature : null;
  }
}

